home *** CD-ROM | disk | FTP | other *** search
/ Ham Radio 2000 / Ham Radio 2000.iso / ham2000 / tcp_ip / ntp_src / ntp_leap.c < prev    next >
C/C++ Source or Header  |  1992-04-03  |  7KB  |  310 lines

  1. /*
  2.  * ntp_leap - maintain leap bits and take action when a leap occurs
  3.  */
  4. #include <stdio.h>
  5.  
  6. #include "global.h"
  7. #include "timer.h"
  8. #include "sockaddr.h"
  9.  
  10. #include "ntp_types.h"
  11. #include "ntp_syslog.h"
  12. #include "ntp_fp.h"
  13. #include "ntp.h"
  14.  
  15. /*
  16.  * This module is devoted to maintaining the leap bits and taking
  17.  * action when a leap second occurs.  It probably has bugs since
  18.  * a leap second has never occurred to excercise the code.
  19.  *
  20.  * The code does two things when a leap second occurs.  It first
  21.  * steps the clock one second in the appropriate direction.  It
  22.  * then informs the reference clock code, if compiled in, that the
  23.  * leap second has occured so that any clocks which need to disable
  24.  * themselves can do so.  This is done within the first few seconds
  25.  * after midnight, UTC.
  26.  *
  27.  * The code maintains two variables which may be written externally,
  28.  * leap_warning and leap_indicator.  Leap_warning can be written
  29.  * any time in the month preceeding a leap second.  24 hours before
  30.  * the leap is to occur, leap_warning's contents are copied to
  31.  * leap_indicator.  The latter is used by reference clocks to set
  32.  * their leap bits.
  33.  *
  34.  * The module normally maintains a timer which is arranged to expire
  35.  * just after 0000Z one day before the leap.  On the day a leap might
  36.  * occur the interrupt is aimed at 2200Z and every 5 minutes thereafter
  37.  * until 1200Z to see if the leap bits appear.
  38.  */
  39.  
  40. /*
  41.  * The leap indicator and leap warning flags.  Set by control messages
  42.  */
  43. u_char leap_indicator;
  44. u_char leap_warning;
  45.  
  46. /*
  47.  * Timer.  Not static only so its value can be given in reply to queries.
  48.  */
  49. /* u_long leap_timer; */
  50. struct timer leap_timer;
  51.  
  52. /*
  53.  * Internal leap bits.  If we see leap bits set during the last
  54.  * hour we set these.
  55.  */
  56. u_char leapbits;
  57. u_char leapseenstratum1;
  58.  
  59. /*
  60.  * Constants.
  61.  */
  62. #define    OKAYTOSETWARNING    (31*24*60*60)
  63. #define    DAYBEFORE        (24*60*60)
  64. #define    TWOHOURSBEFORE        (2*60*60)
  65. #define    FIVEMINUTES        (5*60)
  66. #define    ONEMINUTE        (60)
  67.  
  68. /*
  69.  * Imported from the timer module.
  70.  */
  71. /*u_long current_time;  Now a macro in ntp.h */
  72.  
  73.  
  74. /*
  75.  * Some statistics counters
  76.  */
  77. u_long leap_processcalls;    /* calls to leap_process */
  78. u_long leap_notclose;        /* leap found to be a long time from now */
  79. u_long leap_monthofleap;    /* in the month of a leap */
  80. u_long leap_dayofleap;        /* This is the day of the leap */
  81. u_long leap_hoursfromleap;    /* only 2 hours from leap */
  82. u_long leap_happened;        /* leap process saw the leap */
  83.  
  84. /*
  85.  * Imported from the main module
  86.  */
  87. extern int debug;
  88.  
  89. /*
  90.  * init_leap - initialize the leap module's data.
  91.  */
  92. void
  93. init_leap()
  94. {
  95.     void leap_process __ARGS ((void *)); 
  96.  
  97.     /*
  98.      * Zero the indicators.  Schedule an event for just after
  99.      * initialization so we can sort things out.
  100.      */
  101.     leap_indicator = leap_warning = 0;
  102.     leapbits = 0;
  103.     leapseenstratum1 = 0;
  104.  
  105.     leap_timer.duration = SEC2TICKS(1L << EVENT_TIMEOUT);
  106.     leap_timer.func = leap_process;
  107.     start_timer (&leap_timer);
  108.  
  109.     leap_processcalls = leap_notclose = 0;
  110.     leap_monthofleap = leap_dayofleap = 0;
  111.     leap_hoursfromleap = leap_happened = 0;
  112. }
  113.  
  114.  
  115. /*
  116.  * setnexttimeout - set the next leap alarm
  117.  */
  118. static void
  119. setnexttimeout(secs)
  120.     u_long secs;
  121. {
  122.     /*
  123.      * We try to aim the time out at between 1 and 1+(1<<EVENT_TIMEOUT)
  124.      * seconds after the desired time.
  125.      */
  126. /*    leap_timer = (secs + 1 + (1<<EVENT_TIMEOUT) + current_time)
  127.         & ~((1<<EVENT_TIMEOUT)-1);*/
  128.     leap_timer.duration = SEC2TICKS(secs);
  129.     start_timer (&leap_timer);
  130. }
  131.  
  132.  
  133. /*
  134.  * leap_process - process a leap event expiry and/or a system time step
  135.  */
  136. void
  137. leap_process(v)
  138.      void *v;            /* Argument is not used...  */
  139. {
  140.     u_long leapnext;
  141.     u_long leaplast;
  142.     l_fp ts;
  143.     u_char bits;
  144.     extern struct peer *sys_peer;
  145.     extern u_char sys_leap;
  146.     extern void get_systime();
  147.     extern void calleapwhen();
  148.     extern void step_systime();
  149. #ifdef REFCLOCK
  150.     extern void refclock_leap();
  151. #endif
  152.  
  153.     leap_processcalls++;
  154.     get_systime(&ts);
  155.     calleapwhen(ts.l_ui, &leaplast, &leapnext);
  156.  
  157.     /*
  158.      * Figure out what to do based on how long to the next leap.
  159.      */
  160.     if (leapnext > OKAYTOSETWARNING) {
  161.         if (leaplast < ONEMINUTE) {
  162.             /*
  163.              * The golden moment!  See if there's anything
  164.              * to do.
  165.              */
  166.             leap_happened++;
  167.             bits = 0;
  168.             if (leap_indicator != 0)
  169.                 bits = leap_indicator;
  170.             else if (leapbits != 0)
  171.                 bits = leapbits;
  172.             
  173.             if (bits != 0) {
  174.                 l_fp tmp;
  175.  
  176.                 /*
  177.                  * Step the clock 1 second in the proper
  178.                  * direction.
  179.                  */
  180.                 if (bits == LEAP_DELSECOND)
  181.                     tmp.l_i = 1;
  182.                 else
  183.                     tmp.l_i = -1;
  184.                 tmp.l_uf = 0;
  185.  
  186.                 step_systime(&tmp);
  187. #ifdef REFCLOCK
  188.                 /*
  189.                  * Tell the clocks about it
  190.                  */
  191.                 refclock_leap();
  192. #endif
  193.                 syslog(LOG_INFO,
  194.             "leap second occured, stepped time %s 1 second",
  195.                     tmp.l_i > 0 ? "forward" : "back");
  196.             }
  197.         } else {
  198.             leap_notclose++;
  199.         }
  200.         leap_warning = 0;
  201.     } else {
  202.         if (leapnext > DAYBEFORE)
  203.             leap_monthofleap++;
  204.         else if (leapnext > TWOHOURSBEFORE)
  205.             leap_dayofleap++;
  206.         else
  207.             leap_hoursfromleap++;
  208.     }
  209.  
  210.     if (leapnext > DAYBEFORE) {
  211.         leap_indicator = 0;
  212.         leapbits = 0;
  213.         leapseenstratum1 = 0;
  214.         /*
  215.          * Berkeley's setitimer call does result in alarm
  216.          * signal drift despite rumours to the contrary.
  217.          * Schedule an event no more than 24 hours into
  218.          * the future to allow the event time to be
  219.          * recomputed.
  220.          */
  221.         if ((leapnext - DAYBEFORE) >= DAYBEFORE)
  222.             setnexttimeout((u_long)DAYBEFORE);
  223.         else
  224.             setnexttimeout((u_long)(leapnext - DAYBEFORE));
  225.         return;
  226.     }
  227.  
  228.     /*
  229.      * Here we're in the day of the leap.  Set the leap indicator
  230.      * bits from the warning, if necessary.
  231.      */
  232.     if (leap_indicator == 0 && leap_warning != 0)
  233.         leap_indicator = leap_warning;
  234.     
  235.     if (leapnext > TWOHOURSBEFORE) {
  236.         leapbits = 0;
  237.         leapseenstratum1 = 0;
  238.         setnexttimeout((u_long)(leapnext - TWOHOURSBEFORE));
  239.         return;
  240.     }
  241.  
  242.     /*
  243.      * Here we're in the final 2 hours.  If sys_leap is set, set
  244.      * leapbits to it.
  245.      */
  246.     if (sys_peer != 0 && sys_peer->stratum == STRATUM_REFCLOCK) {
  247.         /*
  248.          * If we're stratum 1, we leap on the basis of
  249.          * leap_indicator only.
  250.          */
  251.         leapseenstratum1 = 1;
  252.         leapbits = 0;
  253.     }
  254.     if (!leapseenstratum1
  255.         && (sys_leap == LEAP_ADDSECOND || sys_leap == LEAP_DELSECOND))
  256.         leapbits = sys_leap;
  257.     setnexttimeout((u_long)((leapnext > FIVEMINUTES)
  258.                 ? FIVEMINUTES : leapnext));
  259. }
  260.  
  261.  
  262. /*
  263.  * leap_setleap - set leap_indicator and/or leap_warning.  Return failure
  264.  *          if we don't want to do it.
  265.  */
  266. int
  267. leap_setleap(indicator, warning)
  268.     int indicator;
  269.     int warning;
  270. {
  271.     u_long leapnext;
  272.     u_long leaplast;
  273.     l_fp ts;
  274.     int i;
  275.     extern void get_systime();
  276.     extern void calleapwhen();
  277.  
  278.     get_systime(&ts);
  279.     calleapwhen(ts.l_ui, &leaplast, &leapnext);
  280.  
  281.     warning &= LEAP_NOTINSYNC;
  282.     indicator &= LEAP_NOTINSYNC;
  283.  
  284.     i = 0;
  285.     if (warning != LEAP_NOTINSYNC)
  286.         if (leapnext > OKAYTOSETWARNING)
  287.             i = 1;
  288.  
  289.     if (indicator != LEAP_NOTINSYNC)
  290.         if (leapnext > DAYBEFORE)
  291.             i = 1;
  292.     
  293.     if (i) {
  294.         syslog(LOG_ERR,
  295.             "attempt to set leap bits at unlikely time of month");
  296.         return 0;
  297.     }
  298.  
  299.     if (warning != LEAP_NOTINSYNC)
  300.         leap_warning = warning;
  301.  
  302.     if (indicator != LEAP_NOTINSYNC) {
  303.         if (indicator == LEAP_NOWARNING) {
  304.             leap_warning = LEAP_NOWARNING;
  305.         }
  306.         leap_indicator = indicator;
  307.     }
  308.     return 1;
  309. }
  310.